/*
 * cryptomfs - a macfuse implementation of cryptofs.
 * Copyright (C) 2008  Greg J. Hedlund
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <CoreFoundation/CoreFoundation.h>

#include <stdio.h>
#include "cryptomfsconfig.h"

static char errString[256] = {0};

void setStringInDictionary(CFMutableDictionaryRef dict, char *key, char *value)
{
	CFStringRef keyRef;
	CFStringRef valueRef;
	
	keyRef = CFStringCreateWithCString(
									   kCFAllocatorDefault, 
									   key, 
									   kCFStringEncodingUTF8 );
	valueRef = CFStringCreateWithCString(
										 kCFAllocatorDefault, 
										 value, 
										 kCFStringEncodingUTF8 );
	
	CFDictionarySetValue( dict, keyRef, valueRef );
	
	CFRelease(keyRef);
	CFRelease(valueRef);
}

void setIntegerInDictionary(CFMutableDictionaryRef dict, char *key, int value)
{
	CFStringRef keyRef;
	CFNumberRef valueRef;
	
	keyRef = CFStringCreateWithCString(
									   kCFAllocatorDefault, 
									   key, 
									   kCFStringEncodingUTF8 );
	valueRef = CFNumberCreate(
							  kCFAllocatorDefault, 
							  kCFNumberIntType, 
							  (void*)&value );
	
	CFDictionarySetValue( dict, keyRef, valueRef );
	
	CFRelease(keyRef);
	CFRelease(valueRef);
}

CFDictionaryRef dictionaryFromConfig(CryptomfsConfig *cfg) 
{
	CFMutableDictionaryRef dict;
	
	dict = CFDictionaryCreateMutable( kCFAllocatorDefault,
									 0,
									 &kCFTypeDictionaryKeyCallBacks,
									 &kCFTypeDictionaryValueCallBacks );
	
	// put items into the dictionary
	setStringInDictionary( dict, "volumename", cfg->volumeName );
	setStringInDictionary( dict, "volumeicon", cfg->volumeIcon );
	setStringInDictionary( dict, "md", cfg->md);
	setStringInDictionary( dict, "cipher", cfg->cipher );
	setStringInDictionary( dict, "passwordhash", cfg->passwordhash );
	setStringInDictionary( dict, "mountpoint", cfg->mountPath ); 
	
	setIntegerInDictionary( dict, "blocksize", cfg->blocksize );
	setIntegerInDictionary( dict, "salts", cfg->salts );
	
	return dict;
}

int getStringFromPropList(CFPropertyListRef propList,
						   const char *propName,
						   char *buffer,
						   int bufferSize)
{
	CFStringRef str;
	CFStringRef propNameRef =
		CFStringCreateWithCString(
								  kCFAllocatorDefault, 
								  propName, 
								  kCFStringEncodingUTF8 );
	
	str = CFDictionaryGetValue( propList, propNameRef );
	CFRelease(propNameRef);
	
	if(str) {
		if(!CFStringGetCString( str, buffer, bufferSize, kCFStringEncodingUTF8 )) {
			return 0;
		}
		return 1;
	}
	
	
	return 0;
}

int getIntegerFromPropList(CFPropertyListRef propList,
						   const char *propName,
						   int *value)
{
	CFNumberRef num;
	CFStringRef propNameRef =
	CFStringCreateWithCString(
							  kCFAllocatorDefault, 
							  propName, 
							  kCFStringEncodingUTF8 );
	
	num = CFDictionaryGetValue( propList, propNameRef );
	CFRelease(propNameRef);
	
	if(num) {
		if(!CFNumberGetValue( num, kCFNumberIntType, (void*)value )) {
			return 0;
		}
		return 1;
	}
	return 0;
}

void createConfigFromDictionary(CryptomfsConfig* cfg, CFPropertyListRef propList)
{
	if(!getStringFromPropList( propList, "volumename", cfg->volumeName, MAXVOLUMENAME ))
	{
		strncpy(cfg->volumeName, DEFAULT_VOLNAME, sizeof(DEFAULT_VOLNAME));
	}
	if(!getStringFromPropList( propList, "volumeicon", cfg->volumeIcon, MAX_PATH ))
	{
		strncpy(cfg->volumeIcon, "", sizeof(""));
	}
	if(!getStringFromPropList( propList, "cipher", cfg->cipher, MAXCIPHERLENGTH ))
	{
		strncpy(cfg->cipher, DEFAULT_CIPHER, sizeof(DEFAULT_CIPHER));
	}
	if(!getStringFromPropList( propList, "md", cfg->md, MAXMDLENGTH ))
	{
		strncpy(cfg->md, DEFAULT_MD, sizeof(DEFAULT_MD));
	}
	if(!getStringFromPropList( propList, "passwordhash", cfg->passwordhash, MAXPASSLENGTH ))
	{
		strncpy(cfg->passwordhash, "", sizeof(""));
	}
	if(!getIntegerFromPropList( propList, "blocksize", &cfg->blocksize ))
	{
		cfg->blocksize = DEFAULT_BLOCKSIZE;
	}
	if(!getIntegerFromPropList( propList, "salts", &cfg->salts ))
	{
		cfg->salts = DEFAULT_SALTS;
	}
	if(!getStringFromPropList( propList, "mountpoint", cfg->mountPath, MAX_PATH ))
	{
		strncpy(cfg->mountPath, DEFAULT_MOUNTROOT, sizeof(DEFAULT_MOUNTROOT));
	}
}

void setupDefaultConfig(CryptomfsConfig *cfg)
{
	strncpy(cfg->volumeName, DEFAULT_VOLNAME, sizeof(DEFAULT_VOLNAME));

	strncpy(cfg->volumeIcon, "", sizeof(""));

	strncpy(cfg->cipher, DEFAULT_CIPHER, sizeof(DEFAULT_CIPHER));

	strncpy(cfg->md, DEFAULT_MD, sizeof(DEFAULT_MD));

	strncpy(cfg->passwordhash, "", sizeof(""));

	cfg->blocksize = DEFAULT_BLOCKSIZE;

	cfg->salts = DEFAULT_SALTS;

	strncpy(cfg->mountPath, DEFAULT_MOUNTROOT, sizeof(DEFAULT_MOUNTROOT));
}


int readConfig(const char* configFile, CryptomfsConfig *cfg)
{
	CFDataRef resourceData;
	CFStringRef errorString;
	SInt32 errorCode;
	CFURLRef fileURL;
	Boolean status;
	CFPropertyListRef propertyList;
	
	CFStringRef cfgFileRef = 
		CFStringCreateWithCString(
								  kCFAllocatorDefault, 
								  configFile, 
								  kCFStringEncodingUTF8 );
	
	if(cfg == NULL) {
		sprintf(errString, "NULL configuration given to readConfig");
		return 0;
	}
	
	
	fileURL =
		CFURLCreateWithFileSystemPath( 
									kCFAllocatorDefault,
									cfgFileRef,
									kCFURLPOSIXPathStyle,
									false );
	CFRelease(cfgFileRef);
	
	status =
		CFURLCreateDataAndPropertiesFromResource(
									kCFAllocatorDefault, fileURL, &resourceData,
									NULL, NULL, &errorCode );
	CFRelease(fileURL);
	
	if(!status) {
		sprintf(errString, "Could not load resource data");
		return 0;
	}
	
	propertyList = 
		CFPropertyListCreateFromXMLData(
									kCFAllocatorDefault,
									resourceData,
									kCFPropertyListImmutable,
									&errorString);
	
	CFRelease(resourceData);
	
	if(propertyList == NULL) {
		int numChars = CFStringGetLength(errorString);
		char *temp = (char*)calloc(numChars, sizeof(char));
		CFStringGetCString(errorString, temp, numChars, kCFStringEncodingASCII);
		fprintf(stderr, "Error: parsing config file - %s\n", temp);
		free(temp);
		return 0;
	}
	
	// read config
	createConfigFromDictionary(cfg, propertyList);
	
	CFRelease(propertyList);
	
	return 1;
}

int writeConfig(const char* configFile, CryptomfsConfig* cfg)
{
	CFPropertyListRef propertyList;
	CFDataRef xmlData;
	Boolean status;
	SInt32 errorCode;
	CFURLRef fileURL;
	CFStringRef errorString;
	CFStringRef cfgFileRef;
	
	cfgFileRef = 
		CFStringCreateWithCString(
								  kCFAllocatorDefault, 
								  configFile, 
								  kCFStringEncodingUTF8 );
	
	// convert the config to a property list
	propertyList = dictionaryFromConfig( cfg );
	
	// convert to xml
	xmlData =
		CFPropertyListCreateXMLData( 
									kCFAllocatorDefault, 
									propertyList );
	CFRelease(propertyList);
	
	// create file url
	fileURL = 
		CFURLCreateWithFileSystemPath(
									  kCFAllocatorDefault,
									  cfgFileRef,
									  kCFURLPOSIXPathStyle,
									  false );
	CFRelease( cfgFileRef );
	
	status = 
		CFURLWriteDataAndPropertiesToResource(
											  fileURL, 
											  xmlData, 
											  NULL, 
											  &errorCode );
	CFRelease(xmlData);
	
	if(!status) {
		sprintf(errString, "Could not write property list");
		return 0;
	}
	
	return 1;
}
